/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package jmodcog2.featuremap.som;

import jmodcog2.base.BaseRunner;

/**
 *
 * @author Mitch
 */
public class SOMRunner extends BaseRunner {

    protected final int input_qty, input_len;
    //I = input_qty, J = input_len
    protected final int som_width, som_height, som_qty;
    //I = som_qty, J = input_len
    @Constant
    protected final float[] som_weights;
    //I = input_qty, J = som_qty
    protected final float[] som_errSq;
    //I = input_qty, J = 2 = coords [x,y]
    protected final float[] winners;


    
    static final int MD_CALC_ERR = 0;
    static final int MD_CALC_WIN = 1;
    @Override
    protected void _run(int mode) {
        int gid = getGlobalId(0);
        if(mode == MD_CALC_ERR) {
            int som_n = gid%input_qty;
            int input_n = gid/input_qty;
            calc_err(input_n, som_n);
        }
        
        if(mode == MD_CALC_WIN) {
            this.calc_win(gid);
        }
    }

    //for-each input, calculate the error for-each som_node
    void calc_err(int input_n, int som_n) {
        float err = 0;
        int i_ind = 0;
        int w_ind = 0;
        //
        i_ind = input_w0_index(input_n);
        w_ind = som_w0_index(som_n);

        for (int w = 0; w < input_len; w++) {
            err += err(w_ind, i_ind, w);
        }
        som_errSq[errSq_index(input_n, som_n)] = err;
    }

    float err(int weight_ind, int input_ind, int w) {
        float t = som_weights[weight_ind + w] - input[input_ind + w];
        return t * t;
    }
    
    
    static final float MAX_F = Float.MAX_VALUE;

    void calc_win(int input_n) {
        int ind = -1;
        float err = MAX_F;
        //
        int e_ind = 0;
        e_ind = errSq_s0_index(input_n);
        for (int som_n = 0; som_n < som_qty; som_n++) {
            float e = som_errSq[e_ind + som_n];
            if (e < err) {
                ind = som_n;
                err = e;
            }
        }

        int wi = win_index(input_n);
        winners[wi] = (0.5f + som_i(ind));// * som_invW;
        winners[wi + 1] = (0.5f + som_j(ind));// * som_invH;
    }

//    //Helper functions
//    int som_ind(int i, int j) {
//        return CMijk_to_n(i, j, 0, som_height, input_len);
//    }
//    
    
   
    int som_w0_index(int som_n) {
        return som_n * input_len;
    }

    int input_w0_index(int input_n) {
        return input_n * input_len;
    }

    //I = input_qty, J = som_qty
    int errSq_index(int input_n, int som_n) {
        return som_n + input_n * som_qty;
    }

    int errSq_s0_index(int input_n) {
        return input_n * som_qty;
    }

    int som_i(int som_n) {
        return this._get2D_i(som_n, som_height);
    }

    int som_j(int som_n) {
        return this._get2D_j(som_n, som_height);
    }
    
    protected int win_index(int input_n) {
        return input_n * 2;
    }
//
//    int input_ind(int in_num) {
//        return CMij_to_n(in_num, 0, input_len);
//    }
//
//    int err_ind(int in_num, int som_i, int som_j) {
//        return CMijk_to_n(in_num, som_i, som_j, som_height, input_qty);
//    }

    @Override
    public float[] getOutput() {
        get(winners);
        return winners;
    }

    @Override
    public boolean process(float[] input) {
        if(input.length == this.input.length) {
            setMode(MD_CALC_ERR);
            put(MODE);
            execute(input_qty);
            setMode(MD_CALC_WIN);
            put(MODE);
            execute(input_qty);            
            return true;
        }
        return false;
    }
}
